package ru.ifmo.mlarinsky.classificator.xml;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import ru.ifmo.mlarinsky.classificator.providers.InstructionsProvider;

public class InstructionsXMLBuilder extends XMLBuilder {

    @Override
    protected Element buildRootElement(Document document, Object dataProvider) throws
            IllegalArgumentException
    {
        if (!(dataProvider instanceof InstructionsProvider)) {
            throw new IllegalArgumentException("data should be "
                    + "InstructionsProvider");
        }

        InstructionsProvider provider = (InstructionsProvider) dataProvider;

        Element rootElement = document.createElement("instructions");
        rootElement.appendChild(buildInputElement(document, provider));
        rootElement.appendChild(buildInitializationElement(document, provider));
        rootElement.appendChild(buildTrainingElement(document, provider));
        rootElement.appendChild(buildOutputElement(document, provider));

        return rootElement;
    }

    @Override
    protected String buildHeader() {
        StringBuilder headerBuilder = new StringBuilder();
        headerBuilder.append("<?xml version='1.0' encoding='UTF-8' ?>");
        headerBuilder.append("<!DOCTYPE instructions [");
        headerBuilder.append("<!ELEMENT instructions (input,initialization,training+,output)>");
        headerBuilder.append("<!ELEMENT input (file)>");
        headerBuilder.append("<!ELEMENT file (#PCDATA)>");
        headerBuilder.append("<!ELEMENT initialization (normalization,x.dimension,y.dimension,lattice,neighbourhood)>");
        headerBuilder.append("<!ELEMENT normalization EMPTY>");
        headerBuilder.append("<!ELEMENT x.dimension (#PCDATA)>");
        headerBuilder.append("<!ELEMENT y.dimension (#PCDATA)>");
        headerBuilder.append("<!ELEMENT lattice EMPTY>");
        headerBuilder.append("<!ELEMENT neighbourhood EMPTY>");
        headerBuilder.append("<!ELEMENT training (steps,lrate,radius)>");
        headerBuilder.append("<!ELEMENT steps (#PCDATA)>");
        headerBuilder.append("<!ELEMENT lrate (#PCDATA)>");
        headerBuilder.append("<!ELEMENT radius (#PCDATA)>");
        headerBuilder.append("<!ELEMENT output (folder,identifier,type+)>");
        headerBuilder.append("<!ELEMENT folder (#PCDATA)>");
        headerBuilder.append("<!ELEMENT identifier (#PCDATA)>");
        headerBuilder.append("<!ELEMENT type EMPTY>");
        headerBuilder.append("<!ATTLIST normalization used (true|false) #REQUIRED>");
        headerBuilder.append("<!ATTLIST lattice type (hexagonal|rectangular) #REQUIRED>");
        headerBuilder.append("<!ATTLIST neighbourhood type (gaussian|step) #REQUIRED>");
        headerBuilder.append("<!ATTLIST lrate type (exponential|linear|inverse) #REQUIRED>");
        headerBuilder.append("<!ATTLIST type format (xml|svg|pdf) #REQUIRED>");
        headerBuilder.append("<!ATTLIST output paper (a4|letter) #REQUIRED>");
        headerBuilder.append("<!ATTLIST output orientation (portrait|landscape) #REQUIRED>");
        headerBuilder.append("]>");

        return headerBuilder.toString();
    }

    private Element buildInputElement(Document document,
            InstructionsProvider provider)
    {
        Element inputElement = document.createElement("input");
        Element fileElement = document.createElement("file");
        Text fileName = document.createTextNode(provider.getInputDataFile());
        fileElement.appendChild(fileName);
        inputElement.appendChild(fileElement);

        return inputElement;
    }

    private Element buildInitializationElement(Document document,
            InstructionsProvider provider)
    {
        Element inititalzationElement = document.createElement("initialization");

        Element normalization = document.createElement("normalization");
        normalization.setAttribute("used",
                String.valueOf(provider.isNormalizationUsed()).toLowerCase());

        Element xDim = document.createElement("x.dimension");
        Text xDimValue = document.createTextNode(
                String.valueOf(provider.getXDimension()));
        xDim.appendChild(xDimValue);

        Element yDim = document.createElement("y.dimension");
        Text yDimValue = document.createTextNode(
                String.valueOf(provider.getYDimension()));
        yDim.appendChild(yDimValue);

        Element lattice = document.createElement("lattice");
        lattice.setAttribute("type", provider.getLatticeType().toString());

        Element neighbourhood = document.createElement("neighbourhood");
        neighbourhood.setAttribute("type", provider.getNeighbourhoodType().toString());

        inititalzationElement.appendChild(normalization);
        inititalzationElement.appendChild(xDim);
        inititalzationElement.appendChild(yDim);
        inititalzationElement.appendChild(lattice);
        inititalzationElement.appendChild(neighbourhood);

        return inititalzationElement;
    }

    private Element buildTrainingElement(Document document,
            InstructionsProvider provider)
    {
        Element trainingElement = document.createElement("training");

        Element steps = document.createElement("steps");
        Text stepsValue = document.createTextNode(String.valueOf(provider.getSteps()));
        steps.appendChild(stepsValue);

        Element learningRate = document.createElement("lrate");
        Text learningRateValue = document.createTextNode(
                String.valueOf(provider.getLearningRate()));
        learningRate.setAttribute("type", provider.getLearningRateType().toString());
        learningRate.appendChild(learningRateValue);

        Element radius = document.createElement("radius");
        Text radiusValue = document.createTextNode(String.valueOf(provider.getRadius()));
        radius.appendChild(radiusValue);

        trainingElement.appendChild(steps);
        trainingElement.appendChild(learningRate);
        trainingElement.appendChild(radius);

        return trainingElement;
    }

    private Element buildOutputElement(Document document,
            InstructionsProvider provider)
    {
        Element outputElement = document.createElement("output");

        Element folder = document.createElement("folder");
        Text folderValue = document.createTextNode(provider.getOutputMapFolder());
        folder.appendChild(folderValue);

        Element identifier = document.createElement("identifier");
        Text identifierValue = document.createTextNode(provider.getOutputMapName());
        identifier.appendChild(identifierValue);

        outputElement.setAttribute("paper", "a4");
        outputElement.setAttribute("orientation", "portrait");
        outputElement.appendChild(folder);
        outputElement.appendChild(identifier);

        for (InstructionsProvider.Format format : provider.getFormats()) {
            Element type = document.createElement("type");
            type.setAttribute("format", format.toString());
            outputElement.appendChild(type);
        }

        return outputElement;
    }

    ////////////////////////////////////////////////////////////////
    /////////////////// Testing ////////////////////////////////////

    public static void main(String[] args) {
        testBuildXMLString();
    }

    private static void testBuildXMLString() {
        InstructionsProvider provider = InstructionsProvider.DEFAULTS;

        InstructionsXMLBuilder builder = new InstructionsXMLBuilder();

        String xml = null;
        try {
            xml = builder.buildXMLString(provider);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(xml);
    }
}
